<?php
/**
* phpCan - http://idc.anavallasuiza.com/
*
* phpCan is released under the GNU Affero GPL version 3
*
* More information at license.txt
*/

namespace PHPCan\Cache;

defined('ANS') or die();

class Cache
{
    private $Debug;
    private $Memory;
    private $suffix;
    private $prefix;
    private $base_prefix;
    private $interface;
    private $settings;
    private $type;
    private $interfaces = array('apc', 'memcache', 'memcached', 'files');

    /**
     * public function __construct ([string $autoglobal])
     *
     * return none
     */
    public function __construct ($autoglobal = '')
    {
        global $Debug, $Config;

        $this->Debug = $Debug;
        $this->settings = $Config->cache;

        if ($autoglobal) {
            $Config->config['autoglobal'][] = $autoglobal;
        }

        $this->base_prefix = md5(__FILE__);
        $this->Memory = array();
    }

    /**
    * public function setType (string $type)
    *
    * Set the execution settings
    *
    * return object
    */
    public function setType ($type, $settings = array())
    {
        if ($settings) {
            $this->setSettings($type, $settings);
        }

        $settings = $this->settings['types'][$type];

        $this->type = $type;
        $this->prefix = $type.'_';
        $this->interface = $settings['interface'];

        if (!$settings || !$this->interface || !$settings['expire']) {
            $this->Memory[$this->interface] = false;

            return $this;
        }

        if (isset($this->Memory[$this->interface])) {
            return $this;
        }

        $this->setInterface($this->interface, $settings);

        return $this;
    }

    /**
    * public function setSettings (string $type, [array $settings])
    *
    * Set the execution settings
    */
    public function setSettings ($type, $settings = array())
    {
        $current = $this->settings['types'][$type];
        $current = is_array($current) ? array_merge($current, (array) $settings) : (array) $settings;

        $this->settings['types'][$type] = $current;
    }

    /**
    * public function getSettings ([string $key])
    *
    * Return current interface settings
    *
    * return array
    */
    public function getSettings ($key = null)
    {
        return $key ? $this->settings['types'][$this->type] : $this->settings['types'][$this->type][$key];
    }

    /**
    * public function setInterface (string $interface, [array $settings])
    *
    * Load the cache method using configuration preferences
    *
    * return boolean
    */
    public function setInterface ($interface, $settings = array())
    {
        $this->interface = $interface;

        if (isset($this->Memory[$interface])) {
            return $this->Memory[$interface] ? true : false;
        }

        if (!in_array($interface, $this->interfaces)) {
            return $this->Memory[$interface] = false;
        }

        if (($interface != 'files') && !extension_loaded($interface)) {
            return $this->Memory[$interface] = false;
        }

        $class = '\\PHPCan\\Cache\\'.ucfirst($interface);

        $this->Memory[$interface] = new $class(ucfirst($interface), $settings);
        $this->Memory[$interface] = $this->Memory[$interface]->connect($this->type);

        return $this->Memory[$interface] ? true : false;
    }

    /**
    * public function setSuffix (string $suffix)
    *
    * Set suffix to cache key
    *
    * return mixed
    */
    public function setSuffix ($suffix)
    {
        return $this->suffix = $suffix;
    }

    /**
    * public function setPrefix (string $suffix)
    *
    * Set prefix to cache key
    *
    * return mixed
    */
    public function setPrefix ($prefix)
    {
        return $this->prefix = $prefix;
    }

    /**
    * public function exists (string $key)
    *
    * Check if exists a variable into cache
    *
    * return mixed
    */
    public function exists ($key)
    {
        if (!$this->Memory[$this->interface]) {
            return false;
        }

        return $this->Memory[$this->interface]->exists($this->base_prefix.$this->prefix.$key.$this->suffix);
    }

    /**
    * public function set (string $key, mixed $value, [integer $expire])
    *
    * Set a variable into memory
    *
    * return mixed
    */
    public function set ($key, $value, $expire = null)
    {
        if (!$this->Memory[$this->interface]) {
            return $value;
        }

        $expire = is_integer($expire) ? $expire : $this->settings['types'][$this->type]['expire'];

        return $this->Memory[$this->interface]->set($this->base_prefix.$this->prefix.$key.$this->suffix, $value, $expire);
    }

    /**
    * public function get (string $key)
    *
    * Get a variable from memory
    *
    * return mixed
    */
    public function get ($key)
    {
        if (!$this->Memory[$this->interface]) {
            return null;
        }

        return $this->Memory[$this->interface]->get($this->base_prefix.$this->prefix.$key.$this->suffix);
    }

    /**
    * public function delete (string $key)
    *
    * Delete a variable from memory
    *
    * return boolean
    */
    public function delete ($key)
    {
        if (!$this->Memory[$this->interface]) {
            return null;
        }

        return $this->Memory[$this->interface]->delete($this->base_prefix.$this->prefix.$key.$this->suffix);
    }

    /**
    * public function clear (void)
    *
    * Clear memory cache
    *
    * return boolean
    */
    public function clear ()
    {
        if (!$this->Memory[$this->interface]) {
            return null;
        }

        return $this->Memory[$this->interface]->clear();
    }

    /**
    * public function expire (string $key)
    *
    * Get time to a key expire
    *
    * return integer
    */
    public function expire ($key)
    {
        if (!$this->Memory[$this->interface]) {
            return null;
        }

        return $this->Memory[$this->interface]->expire($this->base_prefix.$this->prefix.$key.$this->suffix);
    }

    /**
    * public function execute (string $function, [mixed $params])
    *
    * Execute a custom method function and return the result
    *
    * return mixed
    */
    public function execute ($function, $params = null)
    {
        if (!$this->Memory[$this->interface] || !method_exists($this->Memory[$this->interface], $function)) {
            return false;
        }

        return call_user_func_array(array($this->Memory[$this->interface], $function), $params);
    }
}
